package Runde6;

public class Fabrik {
	private static Map fMap = new Map();
	private Map roboMap = new Map();
	private boolean changed;
	private Stats stats;
	
	/**
	 * Constructor.
	 * 
	 * @precondition The name of the new Fabrik has to be unique.
	 * @postcondition Creates a Fabrik-object and adds it to fMap.
	 * @param name The supposedly unique name of the Fabrik to be.
	 * @throws NonUniqueException If the precondition is not met.
	 */
	public Fabrik(String name) throws NonUniqueException {
		try {
			if (fMap.contains(name)) {
				throw new NonUniqueException("Factory with this name already exists!");
			}
			fMap.add(name, this);
		} catch (InvalidTypeException e) {
			//donothing, this WON't happen (sollte vll schicker geloest werden).
		}
		
		//True, damit auf jeden Fall beim ersten Aufruf eine Statistik generiert wird.
		changed = true;
	}
	
	/**
	 * @precondition The Roboter must not be null.
	 * @postcondition Adds the specified Roboter to the Fabrik. 
	 * @param r The Roboter to add.
	 */
	public void addRoboter(Roboter r) {
		try {
			roboMap.add(r.getId(), r);
			changed = true;
		} catch (InvalidTypeException e) {
			System.out.println(e);
		}
	}
	
	/**
	 * @postcondition Removes Roboter from the Fabrik.
	 * @param id The unique ID of the robot to remove.
	 */
	public void removeRoboter(int id) {	
		roboMap.remove(id);
		changed = true;
	}
	
	
	/**
	 * @postcondition Returns the Roboter with the specified ID. If the Roboter does not exist (in this Fabrik), null will be returned.
	 * @param id The ID of the Roboter to fetch.
	 */
	public Roboter getRoboter(int id) {
		//Die Funktionalitaet, die Robotereigenschaften ueber die Fabrik aendern zu koennen,
		//steckt hier. Man bekommt den gewuenschten Roboter zurueckgeliefert, dessen Methoden
		//man aufrufen kann. Da sich somit die Statistik auch aendern kann, ist auch diese
		//Methode nicht "stabil" im Bezug auf jene.
		changed = true;
		return (Roboter) roboMap.get(id);
	}	
	
	/**
	 * @precondition roboMap must not be empty!
	 * @postcondition: Returns the precalculated statistic for the specified trait.
	 */
	public String statStunden() {
		if (changed) {
			stats = new Stats();
			changed = false;
		}
		
		return stats.betriebsstunden();
	}
	
	/**
	 * @precondition roboMap must not be empty!
	 * @postcondition: Returns the precalculated statistic for the specified trait.
	 */
	public String statRotationen() {
		if (changed) {
			stats = new Stats();
			changed = false;
		}
		
		return stats.rotationen();
	}
	
	/**
	 * @precondition roboMap must not be empty!
	 * @postcondition: Returns the precalculated statistic for the specified trait.
	 */
	public String statStrecke() {
		if (changed) {
			stats = new Stats();
			changed = false;
		}
		
		return stats.strecke();
	}
	
	/**
	 * @precondition roboMap must not be empty!
	 * @postcondition: Returns the precalculated statistic for the specified trait.
	 */
	public String statTemp() {
		if (changed) {
			stats = new Stats();
			changed = false;
		}
		
		return stats.temperatur();
	}
	
	/**
	 * @precondition roboMap must not be empty!
	 * @postcondition: Returns the precalculated statistic for the specified trait.
	 */
	//In der Angabe steht hiervon nichts, aber wir sind davon ausgegangen, dass der letzte Punkt ein Fehler ist.
	public String statKapazitaet() {
		if (changed) {
			stats = new Stats();
			changed = false;
		}
		
		return stats.kapazitaet();
	}
	
	
	
	private class Stats {
		
		//Da es so viele Variablen sind, wurden sie nicht erst im Konstruktor initialisiert, sondern direkt.
		private double armSchweissStd = 0;
		private double armLackStd = 0;
		
		private double raupenSchweissStd = 0;
		private double raupenLackStd = 0;
		
		private int armSchweissAnz = 0;
		private int armLackAnz = 0;
		
		private int raupenSchweissAnz = 0;
		private int raupenLackAnz = 0;
		
		private int schweissRot = 0;
		private int lackRot = 0;
		
		private double schweissStrecke = 0;
		private double lackStrecke = 0;
		
		private int armMinTemp = Integer.MAX_VALUE;
		private int armMaxTemp = Integer.MIN_VALUE;
		
		private int raupenMinTemp = Integer.MAX_VALUE;
		private int raupenMaxTemp = Integer.MIN_VALUE;
		
		private double armMinFass = Double.MAX_VALUE;
		private double armMaxFass = Double.MIN_VALUE;
		
		private double raupenMinFass = Double.MAX_VALUE;
		private double raupenMaxFass = Double.MIN_VALUE;			
		
		private Roboter temprob;
		
		/**
		 * @precondition roboMap must not be empty!
		 * @postcondition The variables are ready to use.
		 */
		public Stats() {
			Iter it = roboMap.iterator();
			
			//An dieser Stelle werden alle Roboter durchlaufen und gleich alle Werte gespeichert.
			//Dabei spart man sich das Iterieren und den dazugehoerigen Code in den Einzelprozeduren,
			//falls nichts veraendert wurde. Falls doch, hat man aber nichts verloren, da man sowieso
			//haette ueber alle Roboter iterieren muessen, und hat dennoch eine Codewiederverwendung erwirkt.
			while (it.hasNext()) {
				temprob = (Roboter) it.next();
				
				if (temprob instanceof RoboSchwenkarm) {
					if (temprob.getRolle() instanceof RolleSchweiss) {
						//Anzahl der schweissenden Schwenkarmroboter.
						armSchweissAnz++;
						//Betriebsstunden der schweissenden Schwenkarmroboter.
						armSchweissStd += temprob.getBetriebsSt();
						
						//Rotationen der schweissenden Schwenkarmroboter.
						schweissRot += ((RoboSchwenkarm) temprob).getRotationen();
						
						//Mindest-...
						if (temprob.getRolle().getAttribut() < armMinTemp) {
							armMinTemp = (int) temprob.getRolle().getAttribut();
						}
						//...und Hoechsttemperatur der schweissenden Schwenkarmroboter.
						if (temprob.getRolle().getAttribut() > armMaxTemp) {
							armMaxTemp = (int) temprob.getRolle().getAttribut();
						}
					} else {
						//Anzahl der lackierenden Schwenkarmroboter.
						armLackAnz++;
						//Betriebsstunden der lackierenden Schwenkarmroboter.
						armLackStd += temprob.getBetriebsSt();
						
						//Rotationen der lackierenden Schwenkarmroboter.
						lackRot += ((RoboSchwenkarm) temprob).getRotationen();
						
						//Mindest-...
						if (temprob.getRolle().getAttribut() < armMinFass) {
							armMinFass = (int) temprob.getRolle().getAttribut();
						}
						//...und Hoechstfassungskapazitaet eines lackierenden Schwenkarmtoboters.
						if (temprob.getRolle().getAttribut() > armMaxFass) {
							armMaxFass = (int) temprob.getRolle().getAttribut();
						}
					}
				} else {
					if (temprob.getRolle() instanceof RolleSchweiss) {
						//Anzahl der schweissenden Raupenroboter.
						raupenSchweissAnz++;
						//Betriebsstunden der schweissenden Raupenroboter.
						raupenSchweissStd += temprob.getBetriebsSt();
						
						//Zurueckgelegte Strecke der schweissenden Raupenroboter.
						schweissStrecke += ((RoboRaupe) temprob).getWegstrecke();
						
						//Mindest-...
						if (temprob.getRolle().getAttribut() < raupenMinTemp) {
							raupenMinTemp = (int) temprob.getRolle().getAttribut();
						}
						//und Hoechsttemperatur der schweissenden Raupenroboter.
						if (temprob.getRolle().getAttribut() > raupenMaxTemp) {
							raupenMaxTemp = (int) temprob.getRolle().getAttribut();
						}
					} else {
						//Anzahl der lackierenden Raupenroboter.
						raupenLackAnz++;
						//Betriebsstunden der lackierenden Raupenroboter.
						raupenLackStd += temprob.getBetriebsSt();
						
						//Zurueckgelegte Strecke der lackierenden Raupenroboter.
						lackStrecke += ((RoboRaupe) temprob).getWegstrecke();
						
						//Mindest-...
						if (temprob.getRolle().getAttribut() < raupenMinFass) {
							raupenMinFass = (int) temprob.getRolle().getAttribut();
						}
						//... und Hoechstfassungskapazitaet eines lackierenden Raupenroboters.
						if (temprob.getRolle().getAttribut() > raupenMaxFass) {
							raupenMaxFass = (int) temprob.getRolle().getAttribut();
						}
					}
				}
			}
		}
		
		/**
		 * @postcondition Returns 1 if the argument is 0 to avoid division by zero, otherwise just returns the argument.
		 */
		private int ensure (int term) {
			if (term == 0) {
				return 1;
			} else {
				return term;
			}
		}
		
		/**
		 * @precondition roboMap must not be empty!
		 * @postcondition: Returns the precalculated statistic for the specified trait.
		 */
		public String betriebsstunden() {
			//Geforderte Statistik ueber Betriebsstunden wurde zusammengefasst, da nicht explizit nach verschiedenen Methoden gefragt wurde.
			String output = "Durchschnittliche Betriebsdauer...\n";
			output += "...insgesamt: " + ((armSchweissStd + armLackStd + raupenSchweissStd + raupenLackStd) / ensure(armSchweissAnz + armLackAnz + raupenSchweissAnz + raupenLackAnz)) + "\n";
			output += "(nach Robotertyp:)\n";
			output += "...eines Schwenkarmroboters: " + ((armSchweissStd + armLackStd)/ ensure(armSchweissAnz + armLackAnz)) + "\n";
			output += "...eines Raupenroboters: " + ((raupenSchweissStd + raupenLackStd)/ ensure(raupenSchweissAnz + raupenLackAnz)) + "\n";
			output += "(nach Rolle:)\n";
			output += "...eines Schweissroboters: " + ((armSchweissStd + raupenSchweissStd) / ensure(armSchweissAnz + raupenSchweissAnz)) + "\n";
			output += "...eines Lackierroboters: " + ((armLackStd + raupenLackStd) / ensure(armLackAnz + raupenLackAnz)) + "\n";
			
			return output;
		}
		
		/**
		 * @precondition roboMap must not be empty!
		 * @postcondition: Returns the precalculated statistic for the specified trait.
		 */
		public String rotationen() {
			String output = "Durchschnittliche Anzahl an Rotationen...\n";
			output += "...insgesamt: " + ((schweissRot + lackRot) / ensure(armSchweissAnz + armLackAnz)) + "\n";
			output += "...eines Schweissroboters: " + (1.0 * schweissRot / ensure(armSchweissAnz)) + "\n";
			output += "...eines Lackierroboters: " + (1.0 * lackRot / ensure(armLackAnz)) + "\n";
			
			return output;
		}
		
		/**
		 * @precondition roboMap must not be empty!
		 * @postcondition: Returns the precalculated statistic for the specified trait.
		 */
		public String strecke() {
			String output = "Durchschnittlich zurueckgelegte Wegstrecke...\n";
			output += "...insgesamt: " + ((schweissStrecke + lackStrecke) / ensure(raupenSchweissAnz + raupenLackAnz)) + "\n";
			output += "...eines Schweissroboters: " + (schweissStrecke / ensure(raupenSchweissAnz)) + "\n";
			output += "...eines Lackierroboters: " + (lackStrecke / ensure(raupenLackAnz)) + "\n";
			
			return output;
		}
		
		/**
		 * @precondition roboMap must not be empty!
		 * @postcondition: Returns the precalculated statistic for the specified trait. If a trait does not come up, the placeholder values (Integer.MAX_VALUE, Integer.MIN_VALUE) will be displayed.
		 */
		public String temperatur() {
			/*if (armMaxTemp == Integer.MIN_VALUE) {
				armMaxTemp = 0;
			}
			
			if (armMinTemp == Integer.MAX_VALUE) {
				armMinTemp = 0;
			}
			
			if (raupenMaxTemp == Integer.MIN_VALUE) {
				raupenMaxTemp = 0;
			}
			
			if (raupenMinTemp == Integer.MAX_VALUE) {
				raupenMinTemp = 0;
			}*/
			
			int maxTemp, minTemp;
			
			if (armMaxTemp > raupenMaxTemp) {
				maxTemp = armMaxTemp;
			} else {
				maxTemp = raupenMaxTemp;
			}
			
			if (armMinTemp < raupenMinTemp) {
				minTemp = armMinTemp;
			} else {
				minTemp = raupenMinTemp;
			}
			
			String output = "Die minimale und maximale Arbeitstemperatur...\n";
			output += "...insgesamt: " + replaceInt(minTemp) + " | " + replaceInt(maxTemp) + "\n";
			output += "...der Schwenkarmroboter: " + replaceInt(armMinTemp) + " | " + replaceInt(armMaxTemp) + "\n";
			output += "...der Raupenroboter: " + replaceInt(raupenMinTemp) + " | " + replaceInt(raupenMaxTemp) + "\n";
			
			return output;
		}
		
		/**
		 * @postcondition Returns " - " if the temperature still is a placeholder value.
		 */
		private String replaceInt(int n) {
			if (n == Integer.MAX_VALUE || n == Integer.MIN_VALUE) {
				return " - ";
			} else {
				return "" + n;
			}
		}
		
		/**
		 * @postcondition Returns " - " if the temperature still is a placeholder value.
		 */
		private String replaceDouble(double n) {
			if (n == Double.MAX_VALUE || n == Double.MIN_VALUE) {
				return " - ";
			} else {
				return "" + n;
			}
		}
		
		/**
		 * @precondition roboMap must not be empty!
		 * @postcondition: Returns the precalculated statistic for the specified trait. If a trait does not come up, the placeholder values (Double.MAX_VALUE, Double.MIN_VALUE) will be displayed.
		 */
		public String kapazitaet() {
			double maxFass, minFass;
			
			if (armMaxFass > raupenMaxFass) {
				maxFass = armMaxFass;
			} else {
				maxFass = raupenMaxFass;
			}
			
			if (armMinFass < raupenMinFass) {
				minFass = armMinFass;
			} else {
				minFass = raupenMinFass;
			}
			
			String output = "Die minimale und maximale Fassungskapazitaet...\n";
			output += "...insgesamt: " + replaceDouble(minFass) + " | " + replaceDouble(maxFass) + "\n";
			output += "...der Schwenkarmroboter: " + replaceDouble(armMinFass) + " | " + replaceDouble(armMaxFass) + "\n";
			output += "...der Raupenroboter: " + replaceDouble(raupenMinFass) + " | " + replaceDouble(raupenMaxFass) + "\n";
			
			return output;
		}
	}
	
}
